<?php
/**
 *	KLF Framework
 *
 *	Written by Kristian Oye <kristianoye@gmail.com> on Dec 17, 2011
 *	Copyright (C) 2010-2011
 */
namespace KLF\System\Web\MVC
{
	use KLF\KCore;

	use KLF\System\Errors\KInvalidOperationException;

	use KLF\System\KObject;
	use KLF\System\Web\Routing\KRouteContext;
	
	/**
	 *  Class KControllerFactory
	 *
	 *  The object reponsible for creating route controllers.
	 *
	 *  @package KLF\System\Web\MVC
	 *  @access public
	 *  @author Kristian Oye <kristianoye@gmail.com>
	 *  @since 0.1
	 *
	 *  @method KDefaultControllerFactory NewInstance
	 */
	class KControllerFactory extends KObject implements IControllerFactory
	{
		/**
		 * Creates a new controller for servicing a request.
		 * 
		 * @param KRouteContext $context
		 * @param string $controllerName
		 * 
		 * @return \KLF\System\Web\MVC\IController
		 */
		public function CreateController(KControllerContext $context)
		{
			if (!is_null($className = $this->GetControllerClass($context)))
			{
				$reflection = new \ReflectionClass($className);
				
				return $reflection->newInstance($context);
			};
		}
		
		/**
		 * Returns the fully-qualified class name for the controller.
		 * 
		 * @param KRouteContext $context
		 * @param string $controllerName
		 */
		protected function GetControllerClass(KControllerContext $context)
		{
			$config = $context->HttpContext->Application->Config;
			$controllerName = $context->ControllerName;
			$namespaces = array();
			
			if (!$context->RouteData->DataTokens->TryGetValue('namespaces', $namespaces) || 
				!is_array($namespaces) || count($namespaces) == 0)
			{
				$namespace = get_namespace($context->HttpContext->Application);
				$namespaces = array($namespace . DIRECTORY_SEPARATOR . 'Controllers' . DIRECTORY_SEPARATOR . $controllerName);
			}
			
			$classes = array_values(array_filter(array_map(
				function($ns) use ($controllerName)
				{
					if (class_exists($classPath = $ns . DIRECTORY_SEPARATOR . $controllerName))
					{
						return $classPath;
					} 
					else
					{
						return null;
					}
				}, $namespaces), 
				function($path)
				{
					return !is_null($path); 
				}));
				
			switch(count($classes))
			{
				//  No match
				case 0:
					return null;
					
				//  Exact match
				case 1:
					return $classes[0];
					
				//  Ambigious match--multiple classes found
				default:
					throw new KInvalidOperationException(
						KCore::FS("GetControllerClass({controllerName}) resulted in multiple matching classes: {classList}",
							array(
								'controllerName'	=> $controllerName,
								'classList'			=> join(', ', $classes) )));
					break;
			}
		}
	}
}	